import { ElMessageBox } from 'element-plus'
import { hideLoading, showLoading } from '@/plugins/loading'
import { AxiosResponse } from 'axios'
import { Result } from '@/plugins/axios'
import useUserStore from '@/stores/user'
import { Message } from '@/hooks/message'

// declare global {
//   interface Date {
//     format(format: string): string
//   }
// }
//
// /**
//  * 通用的格式化日期方法, 只需要new一个Date对象, 然后.format即可
//  * @param format - 日期格式字符串，其中包含以下特殊字符：
//  *    - YYYY: 四位数年份
//  *    - M:  月份（01-12）
//  *    - MM: 两位数月份（01-12）
//  *    - D:  日期（01-31）
//  *    - DD: 两位数日期（01-31）
//  *    - H:  小时（24小时制，00-23）
//  *    - HH: 两位数小时（24小时制，00-23）
//  *    - h:  小时（12小时制，01-12）
//  *    - hh: 两位数小时（12小时制，01-12）
//  *    - m:  分钟（00-59）
//  *    - mm: 两位数分钟（00-59）
//  *    - s: 数秒（00-59）
//  *    - ss: 两位数秒（00-59）
//  *    - S:  毫秒（000-999）
//  *    - Q:  季度（1-4）
//  *    - W:  中文星期几（日、一、二、三、四、五、六）
//  *    - w:  数字星期几（1-7，1表示星期一，7表示星期日）
//  *    - a:  上下午标识（上午或下午）
//  *    - A:  AM/PM标识（AM或PM）
//  */
// Date.prototype.format = function (format = 'YYYY-MM-DD HH:mm:ss'): string {
//   const o = {
//     'M+': this.getMonth() + 1, // 月份
//     'D+': this.getDate(), // 日
//     'H+': this.getHours(), // 小时（24小时制）
//     'h+': this.getHours() % 12 === 0 ? 12 : this.getHours() % 12, // 小时（12小时制）
//     'm+': this.getMinutes(), // 分
//     's+': this.getSeconds(), // 秒
//     S: this.getMilliseconds(), // 毫秒
//     Q: Math.floor((this.getMonth() + 3) / 3), // 季度
//     W: ['日', '一', '二', '三', '四', '五', '六'][this.getDay()], // 星期几
//     w: this.getDay(), // 星期几
//     a: this.getHours() < 12 ? '上午' : '下午', // 上下午标识
//     A: this.getHours() < 12 ? 'AM' : 'PM', // AM/PM标识
//   }
//
//   const re = /(Y+)/
//   const match = re.exec(format)
//   if (match) {
//     const t = match[1]
//     format = format.replace(
//       t,
//       (this.getFullYear() + '').substring(4 - t.length),
//     )
//   }
//
//   for (const k in o) {
//     const r = o[k].toString()
//     const regx = new RegExp('(' + k + ')')
//     const match = regx.exec(format)
//     if (match) {
//       const t = match[1]
//       format = format.replace(
//         t,
//         t.length === 1 ? r : ('00' + r).substring(('' + r).length),
//       )
//     }
//   }
//   return format
// }

/**
 * 随机数
 * @param maxNum 最大值
 * @param minNum 最小值（默认为0）
 * @returns 在最大值和最小值区间内的随机数
 */
export function $randomNum(maxNum: number, minNum = 0) {
  return Math.round(Math.random() * (maxNum - minNum) + minNum)
}

/**
 * 本地获设置Token方法
 * @param token Token
 */
export function SET_TOKEN(token: string) {
  sessionStorage.setItem('TOKEN', token)
}

/**
 * 本地获取Token方法
 * @constructor
 */
export function GET_TOKEN() {
  return sessionStorage.getItem('TOKEN')
}

/**
 * 本地获删除Token方法
 * @constructor
 */
export function REMOVE_TOKEN() {
  sessionStorage.removeItem('TOKEN')
}

function GetProperty(obj: object, str: string) {
  str = str.replace(/\[(\w+)]/g, '.$1') // 处理数组下标
  const arr = str.split('.')
  for (const i in arr) {
    obj = obj[arr[i]] || ''
  }
  return obj
}

/**
 * 根据表格数据和指定的列属性，计算列的合适宽度
 * 该函数通过分析表格中每一行数据在指定列的文本宽度，来动态调整列宽
 * 这样可以确保列宽足够显示所有文本，同时不会浪费过多空间
 *
 * @param tableData 表格数据，是一个对象数组，每个对象代表一行数据
 * @param prop 指定列的属性，可以是字符串或数字，用于从每行数据中提取文本
 * @param minWidth 列的最小宽度，默认为85，确保列宽不会过窄
 * @param padding 文本的内边距，默认为24，用于计算列宽时考虑文本与列边界的间隔
 * @param childrenProp 子级属性，用于处理嵌套数据
 * @returns 返回计算出的列宽，确保所有文本都能显示且符合最小宽度要求
 */
export function getColumnWidth(
  tableData: Record<string, any>[],
  prop: string,
  minWidth = 85,
  padding = 30,
  childrenProp = 'children',
) {
  return computeColumnWidth(
    tableData,
    prop,
    minWidth,
    padding,
    null,
    childrenProp,
  )
}

// 计算裂宽逻辑封装，数据字典需要映射的增加 needComputeColumnName 方法进行特殊处理
export function computeColumnWidth(
  tableData: Record<string, any>[],
  prop: string,
  minWidth,
  padding,
  getDictionaryNameByValue?: (
    dictionaryValue: string,
    defaultName?: string,
  ) => string,
  childrenProp?: string,
) {
  // 对每行数据，计算指定列的文本宽度加上内边距，以数组形式存储
  const contentWidths = tableData?.map((item) => {
    // 从当前行数据中提取指定列的值，并转换为字符串
    const value = GetProperty(item, prop)
    const valueString = value ? String(value) : ''
    // 最后的值，可能需要特殊处理
    const lastValue = getDictionaryNameByValue
      ? getDictionaryNameByValue(valueString)
      : valueString
    // 计算文本宽度（此处省略getTextWidth函数的实现，假设它能根据文本内容计算宽度）
    const textWidth = getTextWidth(lastValue)

    let childrenTextWidth = 0
    if (childrenProp && item[childrenProp] && item[childrenProp].length > 0) {
      childrenTextWidth = computeColumnWidth(
        item[childrenProp],
        prop,
        minWidth,
        padding,
        getDictionaryNameByValue,
        childrenProp,
      )
    }

    // 返回文本宽度加上内边距，代表该列在当前行需要的宽度
    return Math.max(textWidth, childrenTextWidth) + padding
  }) || [0]

  // 找出所有行中，指定列需要的最大宽度
  const maxWidth = Math.max(...contentWidths)

  // 返回列宽，取最小宽度和内容最大宽度中的较大值，确保列宽合适
  return Math.max(minWidth, maxWidth)
}

/**
 * 计算给定文本在特定样式下的宽度
 * 此函数通过创建一个隐藏的HTML span元素，将文本内容插入其中，然后测量span的宽度来实现
 * 这种方法允许我们考虑文本的自然排版特性，如单词间距和字体样式，而不仅仅是基于固定宽度的字符
 *
 * @param text 要测量宽度的文本字符串
 * @returns 返回文本的宽度，以像素为单位
 */
export function getTextWidth(text: string) {
  // 创建一个span元素，用于测量文本宽度
  const span = document.createElement('span')

  // 设置span为隐藏状态，以避免影响页面布局
  span.style.visibility = 'hidden'
  // 将span定位到页面之外，确保不干扰页面显示
  span.style.position = 'absolute'
  span.style.top = '-9999px'
  // 确保文本不会因为换行而影响宽度计算
  span.style.whiteSpace = 'nowrap'
  // 设置文本的样式，模拟特定的视觉属性
  span.style.fontSize = '14px'
  span.style.fontWeight = '400'
  span.style.lineHeight = '23px'

  // 将文本内容插入span中
  span.innerText = text

  // 将span添加到文档主体中，以便浏览器计算其尺寸
  document.body.appendChild(span)

  // 获取并保存文本的宽度
  const width = span.offsetWidth

  // 从文档中移除span，避免内存泄漏
  document.body.removeChild(span)

  // 返回计算出的文本宽度
  return width
}

/**
 * 删除按钮点击事件处理函数
 * @param row
 * @param deleteFunction 删除函数
 * @param getInfo 获取信息函数
 * @param showPropText 显示的属性文本
 * @param dataText 数据文本
 * @param propText 属性文本
 */
export function handleClickDeleteButton(
  row: any | any[],
  deleteFunction: (ids: string | number) => Promise<AxiosResponse<Result>>,
  getInfo: () => void,
  dataText: string = '数据',
  showPropText: string = 'name',
  propText: string = 'id',
) {
  const isArray = row && row instanceof Array
  let text: string

  if (!isArray) {
    text = `确定要删除 ${row[showPropText]} 吗？`
  } else {
    text = `确定要删除选中的 ${row.length} 条${dataText}吗？`
  }
  ElMessageBox.confirm(text, '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning',
  })
    .then(async () => {
      let ids: string
      showLoading()
      if (!isArray) {
        ids = '' + row[propText]
      } else {
        ids = row.map((item) => item[propText]).join(',')
      }
      const { data } = await deleteFunction(ids)
      if (data.status === 200) {
        Message({
          message: data.message,
          type: 'success',
        })
      }
      hideLoading()
      getInfo()
    })
    .catch(() => {})
}

export function disablePermissionButton(
  selectData: any[],
  permission: string,
  prop = 'unitId',
) {
  if (selectData.length === 0) {
    return true
  } else {
    const userStore = useUserStore()
    const ids = userStore.getPermissionId(permission)
    return (
      !userStore.isAdmin &&
      selectData.some((item: any) => !ids.includes(item[prop]))
    )
  }
}
